# -*- coding: utf-8 -*-
# @Time    : 2024/11/3 下午11:12
# @Author  : xiaowanzi
# @File    : testRunner.py

import json
from datetime import datetime

from base.project_path import get_project_path
from openpyxl import load_workbook
from configobj import ConfigObj
import unittest
import getopt
import time
import sys
import os

from base.public_tools import get_project_run_id, set_glob_project_run_id
from core.test_report_generate.my_beautiful_report import BeautifulReport
from core.test_report_generate.my_report import TestReport

project_path = get_project_path()
conf_ini = project_path + "\\config.ini"
config = ConfigObj(conf_ini, encoding='UTF8')
hj = config['HJ']['hj']  # hj = maoyan


class AllCaseTestRunner(object):
    def __init__(self, file=None):
        if file:
            file_name = config[hj][file]
        else:
            file_name = config[hj]["file"]
        self.file_path = project_path + "\\excel\\%s" % file_name

    # 导入/test_case/下面的测试模块
    @staticmethod
    def find_pyfile_and_import(rootDir):
        """
        :param rootDir: 输入一个根目录
        :return:
        """
        list_dirs = os.walk(rootDir)  # list_dirs 是一个迭代对象
        """
        os.walk()用来遍历一个目录内各个子目录和子文件,返回一个3元组（dirpath, dirnames, filenames）,
        这3个元素分别表示每次遍历的目录路径，目录名字和文件名
        """
        for dirName, subdirlist, filelist in list_dirs:
            for f in filelist:
                file_name = f  # file_name 为各个目录的文件列表，既由各个模块的用例名称组成各个列表。
                if file_name[:5] == "test_" and file_name[-3:] == ".py":  # 判断测试用例格式：以 test_ 开头和 .py 结尾
                    if dirName[-1:] != "/":  # 判断路径最后一个符号是否是“/”
                        """
                        将符号“/”和“\\”都替换成“.”,然后取第三至最后一个字符,若是 dirName 最后一个字符是 "/"，则取
                        第三至最后一个字符前之间的字符,
                        例如传入 rootDir = './test_case/hexintoubao',则
                        dirName = './test_case/hexintoubao'
                        impath = 'test_case.hexintoubao'
                        """
                        impath = dirName.replace("/", ".")[2:].replace("\\", ".")
                    else:
                        impath = dirName.replace("/", ".")[2:-1].replace("\\", ".")
                    if impath != "":
                        # 组装路径，导入用例，例如:from  test_case.hexintoubao.test_hexin_0801 import *
                        exe_str = "from " + impath + "." + file_name[0:-3] + " import *"
                    else:
                        exe_str = "import" + file_name[0:-3]
                    print(exe_str)
                    exec(exe_str, globals())  # 动态导入所指定的测试模块。exec(),动态执行Python代码,返回值永远为None

    # 读取文件，组装测试用例
    def get_xls_case_by_index(self, sheet_name_list):
        # sheet_name_list 为数据文件中的各个页的名称
        file = load_workbook(self.file_path)  # 加载数据文件
        caseList = []
        for sheet_name in sheet_name_list:
            sheet = file[sheet_name]
            for i in range(1, sheet.max_row):  # 从第二行开始读取，第一行是表头字段
                # sheet.rows 是生成器类型，要转换成列表 list(sheet.rows)，每一行形成一个元祖，最后组成一个列表
                # 如 [(<Cell '1359_核心'.A1>, <Cell '1359_核心'.B1>, ...),(<Cell '1359_核心'.A2>, <Cell '1359_核心'.B1>,...)]
                if list(sheet.rows)[i][0].value.strip().upper() == 'YES':  # list(sheet.rows)[i][0] 获取单元格的值
                    ClassName = sheet.cell(i + 1, 3).value
                    caseName = sheet.cell(i + 1, 4).value
                    case = '%s("%s")' % (ClassName.strip(), caseName.strip())
                    # 结果case: TestHeXin1359("test_hexin_1359_01")
                    caseList.append(case)  # 把所有测试用例集合到一个列表中，格式：['TestHeXin1359("test_hexin_1359_01")', ... ]
        return caseList

    # 将测试用例添加到测试套件中
    def get_test_suite(self, testDir, sheet_name_list):
        """
        :param testDir: 所有脚本的顶层目录
        :param sheet_name_list: 想要读取的表名
        """
        test_suite = unittest.TestSuite()  # 先构造测试集
        self.find_pyfile_and_import(testDir)  # 动态导入测试用例
        testCaseList = self.get_xls_case_by_index(sheet_name_list)
        for test_case in testCaseList:
            # eval() 将字符串参数当作Python代码执行，并返回执行结果; testCaseList 中列表的值是字符串表达式，所以要进行转换
            test_suite.addTest(eval(test_case))  # 套件添加用例的格式：testsuite.addTest(test_baidu.BaiduTest('test_baidu'))
        return test_suite

    def get_rerun_test_suite(self, testDir, name):
        """
        :param testDir: 所有脚本的顶层目录
        :param name: 想要读取的表名
        """
        test_suite = unittest.TestSuite()  # 先构造测试集
        self.find_pyfile_and_import(testDir)  # 动态导入测试用例
        project_path = get_project_path()
        rerun_file = project_path + "\\conf\\" + name
        try:
            f = open(rerun_file, 'r')
            testCaseList = json.load(f)
            f.close()
            for test_case in testCaseList:
                # eval()将字符串参数当作Pvthon代码执行，并返回执行结果;testcaseList 中列表的值是字符串表达式，所以要进行转换
                test_suite.addTest(eval(test_case))  # 套件添加用例的格式: testsuite,addTest(test baidu,BaiduTest('test baidu')
            return test_suite
        except Exception as E:
            return None

    # 测试套件中用例转pytest可读用例
    @staticmethod
    def suite2pytestCases(suite):
        cases = []  # 用于存放Pytest支持的用例路径字符串

        def _collect(tests):  # 递归，如果下级元素还是TestSuite则继续往下找
            if isinstance(tests, unittest.TestSuite):
                [_collect(i) for i in tests if tests.countTestCases() != 0]
            else:
                path = tests.id().split(".")  # case.id()可以获取用例路径(字符串)
                lujin = "\\".join(path[0:-3])
                h_list = path[-3:]
                h_list[0] += ".py"
                name = "::".join(h_list)
                cases.append(lujin + "\\" + name)  # 如果下级元素是TestCase，则添加到TestSuite中

        _collect(suite)
        return cases


if __name__ == "__main__":
    begin_time = datetime.strptime(time.strftime("%Y-%m-%d %H:%M:%S"), "%Y-%m-%d %H:%M:%S")
    zd = {
        #  -n 财险第三方 第三方车险
        "财险第三方": {
            "report_name": "test_result_cx_dsf_",
            "testDir": "./core/test_case",
            "file": "cx_dsf_file",
            "sheet_list": ["第三方车险"]
        }
    }
    name = None
    actuator = "unittest"
    rerun = None
    clean = None
    """
    sys.argv[1:] 过滤掉第一个参数，它是执行脚本的名字
    当选项后面是带一个附加参数时，在分析串中写入选项字符同时后面加一个":"号。所以"he:"就表示"h"是一个开关选项；"e:"则表示后面应该带一个参数。
    长命令格式同理
    opts是一个两元组的列表。每个元素为：(选项串, 附加参数)。如果没有附加参数则为空串''
    args为不属于格式信息的剩余的命令行参数,即不是按照getopt(）里面定义的长或短选项字符和附加参数以外的信息
    """
    opts, args = getopt.getopt(sys.argv[1:], '-h-e:-n:-a:-l-c',
                               ['help', 'environment=', 'name=', 'actuator=', 'lf', 'clean'])
    for opt_name, opt_value in opts:
        if opt_name in ('-h', '--help'):
            print("抱歉，暂时无法给您提供帮助！！！")
        if opt_name in ('-e', '--environment'):
            config['HJ']['hj'] = opt_value
            config.write()
            hj = opt_value
        if opt_name in ('-n', '--name'):
            name = opt_value
        if opt_name in ('-a', '--actuator'):
            actuator = opt_value
        if opt_name in ('-l', '--lf'):
            rerun = True
        if opt_name in ('-c', '--clean'):
            clean = True
    if name is None:
        raise ValueError("请输入需要运行的项目名称！！！")
    Run = AllCaseTestRunner(file=zd[name]["file"])
    suite = Run.get_test_suite(zd[name]["testDir"], args)  # 获取测试套件

    """使用unittest方式运行测试用例"""
    tx_dict = {
        "regression": "冒烟-",
        "four": "第4套-",
        "six": "第6套-",
        "eight": "第8套-",
        "nineteen": "第19套-",
    }
    filename = zd[name]["report_name"] + time.strftime("%Y%m%d%H%M%S")  # 拼接形成测试报告文件名，以时间戳进行区分
    report_dir = './reports/%s' % hj  # 拼接形成测试报告的路径，如：./reports/maoyan
    project_run_id = get_project_run_id()
    set_glob_project_run_id(project_run_id)
    result = BeautifulReport(suite)  # BeautifulReport 根据测试套件运行，分析报告数据，主要函数是 report.suites.run()
    description = tx_dict[hj] + name + '自动化测试报告'
    finish_time = datetime.strptime(time.strftime("%Y-%m-%d %H:%M:%S"), "%Y-%m-%d %H:%M:%S")
    report_path = "{}.html".format(hj + '\\' + filename)
    result.report(description=description, filename=filename, report_dir=report_dir, theme='theme_cyan', rerun_name='财险第三方')
    TestReport().test_report(begin_time, finish_time, description, report_path, project_run_id, theme='theme_cyan')
    # alter_test_report("{}.html".format(hj + '\\' + filename))  # 修改CSS文件指定的路径，BeautifulReport 才能在本地渲染报告
    # print(jenkins_show_report(filename, actuator, hj))
